package world;

import math.MathUtil;
import math.Matrix4;
import math.Vector3f;

public class Camera2 {
	// Moving directions
	public static final int FORWARD = 0;
	public static final int BACKWARD = 1;
	public static final int RIGHT = 2;
	public static final int LEFT = 3;
	public static final int UP = 4;
	public static final int DOWN = 5;

	public static final Vector3f UP_POSTION = new Vector3f(0, 1, 0);

	public Vector3f targetPostion;
	public Vector3f rotation;//x pitch, y yaw
	
	public Camera2(Vector3f position, Vector3f rotation) {
		this.targetPostion = position;
		this.rotation = rotation;
	}

	/* Use this when adding rotation instead of Vector3f.add since this function makes the numbers stay under 360. */
	public void rotate(Vector3f rot) {
		rotation.x += rot.x;
		rotation.y += rot.y;
		rotation.z += rot.z;

		if (rotation.x >= 360.0f || rotation.x <= -360.0f){
			rotation.x = rotation.x % 360.0f;
		}

		if (rotation.y >= 360.0f || rotation.y <= -360.0f){
			rotation.y = rotation.y % 360.0f;
		}

		if (rotation.z >= 360.0f || rotation.z <= -360.0f){
			rotation.z = rotation.z % 360.0f;
		}

		if (rotation.x <= -90.0f){
			rotation.x = -90.0f;
		}
		else if (rotation.x >= 90.0f){
			rotation.x = 90.0f;
		}
	}
	
	public void moveTargeForward(float distance) {
		targetPostion.z = targetPostion.z - distance;
	}

	public void moveTargeBackwards(float distance) {
		targetPostion.z = targetPostion.z + distance;
	}

	public void moveTargeLeft(float distance) {
		targetPostion.x = targetPostion.x - distance;
	}

	public void moveTargeRight(float distance) {
		targetPostion.x = targetPostion.x + distance;
	}
	
	public void moveTargeUp(float distance) {
		targetPostion.y = targetPostion.y + distance;
	}

	public void moveTargeDown(float distance) {
		targetPostion.y = targetPostion.y - distance;
	}

	public void rotatUp(float distance) {
		rotation.y = rotation.y + distance;
		rotation.y(MathUtil.clamp(rotation.y(), -88.75f, -1));
	}

	public void roateDown(float distance) {
		rotation.y = rotation.y - distance;
	}

	public void rotatLeft(float distance) {
		rotation.x = rotation.x + distance;
	}

	public void roateRight(float distance) {
		rotation.x = rotation.x - distance;
	}

	public void zoomIn(float distance) {
		rotation.z = rotation.z + distance;
		rotation.z(rotation.z() > 5 ? rotation.z() : 5);
	}

	public void zoomOut(float distance) {
		rotation.z = rotation.z - distance;
	}
	
	public Matrix4 calcLookAtMatrix() {
		Vector3f cameraPoint = resolvePosition();
		Vector3f lookPoint = targetPostion;
		Vector3f upPoint = UP_POSTION;
		
		Vector3f lookDir = new Vector3f(lookPoint).sub(cameraPoint).normalize();
		Vector3f upDir = new Vector3f(upPoint).normalize();

		Vector3f rightDir = lookDir.cross(upDir).normalize();
		Vector3f perpUpDir = rightDir.cross(lookDir);

		Matrix4 rotMat = new Matrix4().clearToIdentity();
		rotMat.putColumn(0, rightDir, 0);
		rotMat.putColumn(1, perpUpDir, 0);
		rotMat.putColumn(2, lookDir.mult(-1), 0);

		return rotMat.transpose().translate(new Vector3f(cameraPoint).mult(-1));
	}	

	private Vector3f resolvePosition() {
		double phi = rotation.x() * Math.PI / 180;
		double theta = (rotation.y() + 90) * Math.PI / 180;

		float sinTheta = (float) Math.sin(theta);
		float cosTheta = (float) Math.cos(theta);
		float cosPhi = (float) Math.cos(phi);
		float sinPhi = (float) Math.sin(phi);
		return new Vector3f(sinTheta * cosPhi, cosTheta, sinTheta * sinPhi).mult(rotation.z()).add(targetPostion);
	}
}